package com.tumblr.logico2012;
import processing.core.PApplet;
import processing.core.PImage;
import SimpleOpenNI.IntVector;
import SimpleOpenNI.SimpleOpenNI;
import SimpleOpenNI.SkeletonCapability;
import SimpleOpenNI.XnVSelectableSlider2D;
import SimpleOpenNI.XnVSessionManager;

import com.tumblr.logico2012.gestures.GestureManager;
import com.tumblr.logico2012.screens.ILabyrinthScreen;
import com.tumblr.logico2012.screens.IScreen;
import com.tumblr.logico2012.screens.ISelectGameScreen;
import com.tumblr.logico2012.screens.IShowGameScreen;
import com.tumblr.logico2012.screens.IStartScreen;
import com.tumblr.logico2012.screens.PopupManager;
import com.tumblr.logico2012.screens.PopupScreen;

/**
 * AppletLogico is the main class of the Logico program.
 */
@SuppressWarnings("serial")
public class AppletLogico extends PApplet implements IApplet {
	//public final static boolean DISABLE_CAMERA = true;
	public final static boolean DISABLE_CAMERA = false;
	
	public final static int SCREENWIDTH = 1000;
	public final static int SCREENHEIGHT = 600;
	
	// GAMES
	int numGames = 8;
	PImage img[] = new PImage[numGames];
	String title[] = {"Labyrinth", "Sudoku", "Quiz", "Pong", "Labyrinth 2", "Sudoku 2", "Quiz 2", "Pong 2"};

	// CONTEXT
	private GestureManager m_manager;
	private SimpleOpenNI  m_context;	
	private IScreen m_screen;
	private boolean m_drawSkeleton;
	private PopupManager pm;

	// NITE
	private XnVSessionManager     sessionManager;
	private XnVSelectableSlider2D trackPad;
	

	/**
	 * First screen
	 */
	private class StartScreen extends IStartScreen {
		public StartScreen(PApplet processor) {
			super(processor);
		}

		@Override
		public void leave() {
			// when leaving, we go to the "select game" screen.
			setScreen(new SelectGameScreen(AppletLogico.this, img, title));
		}		
	}
	
	/**
	 * Screen: select game
 	 */
	private class SelectGameScreen extends ISelectGameScreen {
		public SelectGameScreen(PApplet processor, PImage[] images, String[] titles) {
			super(processor, m_manager, images, titles);
		}

		@Override
		public void leave() {
			// We open the selected game
			int selected = new Integer(returnValue());
			if(selected == 0 || selected == 4) {
				setScreen(new LabyrinthScreen(AppletLogico.this, m_manager, 1));
			} else {
				setScreen(new ShowGameScreen(AppletLogico.this, img[selected % numGames], title[selected % numGames]));
			}
		}
	}
	
	/**
	 * Screen: dummy game
	 */
	private class ShowGameScreen extends IShowGameScreen {
		public ShowGameScreen(PApplet processor, PImage img, String title) {
			super(processor, m_manager, img, title);
		}

		@Override
		public void leave() {
			// back to the select screen
			setScreen(new SelectGameScreen(AppletLogico.this, img, title));
		}		
	}

	/**
	 * Screen: labyrinth
	 */
	class LabyrinthScreen extends ILabyrinthScreen {
		public LabyrinthScreen(PApplet processor, GestureManager manager, int level) {
			super(AppletLogico.this, processor, AppletLogico.this.m_context, manager, level);
		}

		@Override
		public void leave() {
			// back to the select screen
			if(!returnValue().equals(FREE+"") && m_level + 1 <= LABYRINTH.length)
				setScreen(new LabyrinthScreen(p, m_manager, m_level+1));
			else
				setScreen(new SelectGameScreen(AppletLogico.this, img, title));
		}		
	}

	/**
	 * Set a screen as current screen
	 * @param screen Screen
	 */
	void setScreen(IScreen screen) {
		m_screen = screen;
		m_manager.reset();
	}

	/**
	 * Return the current screen
	 * @return Screen
	 */
	@Override
	public IScreen getCurrentScreen() { return m_screen; }
	
	/**
	 * Draw the skeleton for the specified user
	 * @param userId User
	 */
	public void drawSkeleton(int userId)
	{
		stroke(255,0,0);
		strokeWeight(3);
		
		m_context.drawLimb(userId, SimpleOpenNI.SKEL_HEAD, SimpleOpenNI.SKEL_NECK);

		m_context.drawLimb(userId, SimpleOpenNI.SKEL_NECK, SimpleOpenNI.SKEL_LEFT_SHOULDER);
		m_context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_SHOULDER, SimpleOpenNI.SKEL_LEFT_ELBOW);
		m_context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_ELBOW, SimpleOpenNI.SKEL_LEFT_HAND);

		m_context.drawLimb(userId, SimpleOpenNI.SKEL_NECK, SimpleOpenNI.SKEL_RIGHT_SHOULDER);
		m_context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_SHOULDER, SimpleOpenNI.SKEL_RIGHT_ELBOW);
		m_context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_ELBOW, SimpleOpenNI.SKEL_RIGHT_HAND);

		m_context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_SHOULDER, SimpleOpenNI.SKEL_TORSO);
		m_context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_SHOULDER, SimpleOpenNI.SKEL_TORSO);

		m_context.drawLimb(userId, SimpleOpenNI.SKEL_TORSO, SimpleOpenNI.SKEL_LEFT_HIP);
		m_context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_HIP, SimpleOpenNI.SKEL_LEFT_KNEE);
		m_context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_KNEE, SimpleOpenNI.SKEL_LEFT_FOOT);

		m_context.drawLimb(userId, SimpleOpenNI.SKEL_TORSO, SimpleOpenNI.SKEL_RIGHT_HIP);
		m_context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_HIP, SimpleOpenNI.SKEL_RIGHT_KNEE);
		m_context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_KNEE, SimpleOpenNI.SKEL_RIGHT_FOOT);  
	}

	void onItemHover(int nXIndex,int nYIndex)
	{
		textAlign(CENTER,CENTER);
		textSize(55);
		if (nXIndex==0) text("PLayer 1");
		if (nXIndex==1) text("PLayer 2");
		
	  println("onItemHover: Player" + nYIndex+1);

	}

	/**
	 * Register a new popup with the popup manager.
	 * @param popup New popup
	 */
	@Override
	public void addPopup(PopupScreen popup) {
		pm.addPopup(popup);
	}
	
	// --------------- Processing methods

	public void setup()
	{
		
		pm = new PopupManager(this);
		pm.init();
		
		size(SCREENWIDTH, SCREENHEIGHT, OPENGL);
		
		noStroke();
		smooth();
		
		m_drawSkeleton = false;

		// SimpleOpenNI
		m_context = new SimpleOpenNI(this);
		if(!DISABLE_CAMERA) {
			m_context.setMirror(true);
			m_context.enableDepth();
			m_context.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL);
			m_context.enableHands();
		}
		
		// Gesture manager
		m_manager = new GestureManager(this, this, m_context);
	
		// Pictures
		for(int i = 0; i < numGames; i++)
		{
			img[i] = loadImage((i+1) + ".jpeg", "jpg");
		}
		
		// Go to first screen
		setScreen(new StartScreen(AppletLogico.this));
			
		sessionManager = m_context.createSessionManager("Click,Wave", "RaiseHand");
		trackPad = new XnVSelectableSlider2D(2,1,100,200);
		sessionManager.AddListener(trackPad);
		trackPad.RegisterItemHover(this);
	}
	
	public void draw()
	{
		background(0);
		if(!DISABLE_CAMERA) {
			m_context.update();
			m_context.update(sessionManager);
		}
		m_manager.update();

		pushMatrix();
		m_manager.setDrawingArea();
		m_screen.draw();
		popMatrix();
		
		m_manager.drawGestureBars();

		if(m_drawSkeleton) {
			IntVector aUsers = new IntVector();
			m_context.getUsers(aUsers);
			for(int i = 0; i < aUsers.size(); i++) {
				drawSkeleton(aUsers.get(i));
			}
		}
		
		pm.draw();
	}

	@Override	
	public void keyPressed() {
		if(key == 's') { // enable/disable skeleton
			m_drawSkeleton = !m_drawSkeleton;
		}
		
		m_screen.keyPressed();
	}
	
	// -----------------------------------------------------------------
	// SimpleOpenNI events

	public void onNewUser(int userId)
	{
	  println("onNewUser - userId: " + userId);

	  SkeletonCapability cap = m_context.getUserGenerator().GetSkeletonCap();
	  if(cap.NeedPoseForCalibration()) {
		  // automatic calibration not supported: we use a pose
		  println("  start pose detection");
		  m_context.startPoseDetection("Psi",userId);
	  } else {
		  // automatic calibration
		  println("  calibration without pose");
		  cap.RequestCalibration(userId, true);
	  }
	}

	public void onLostUser(int userId)
	{
	  println("onLostUser - userId: " + userId);
	  // We signal the Gesture Manager: all information collected for this user
	  // will be deleted
	  m_manager.unregisterUser(userId);
	  
	  // If there are no more user, we go back to the first screen.
	  if(m_context.getNumberOfUsers() < 1) {
		  setScreen(new StartScreen(this));
	  }
	}

	public void onStartPose(String pose,int userId)
	{
	  println("onStartPose - userId: " + userId + ", pose: " + pose);
	  println(" stop pose detection");
	  
	  m_context.stopPoseDetection(userId); 
	  m_context.requestCalibrationSkeleton(userId, true);
	 
	}

	public void onEndPose(String pose,int userId)
	{
	  println("onEndPose - userId: " + userId + ", pose: " + pose);
	}
	
	public void onStartCalibration(int userId)
	{
	  println("onStartCalibration - userId: " + userId);
	}
	
	public void onEndCalibration(int userId, boolean successfull)
	{
	  println("onEndCalibration - userId: " + userId + ", successfull: " + successfull);
	  
	  if (successfull) 
	  { 
	    println("  User calibrated !!!");
	    m_context.startTrackingSkeleton(userId); 

	    // The program is now calibrated for the user: we activate the
	    // gesture for him/her (depending which screen is currently active)
	    m_screen.registerUser(m_manager, userId);

	    // If we are on the first screen (waiting for a user), we can now start
	    if(m_screen.getClass() == StartScreen.class) {
	    	m_screen.leave();
			m_manager.reset();
	    }
	  } 
	  else 
	  { 
		  // automatic calibration failed: we try a pose detection
	    println("  Failed to calibrate user !!!");
	    println("  Start pose detection");
	    m_context.startPoseDetection("Psi",userId);
	  }
	}
}
